
\writestatus{loading}{Go/WeiQi/Baduk Lua module}


\startluacode

Game = [[
(;CA[Windows-1252]SZ[19]AP[MultiGo:4.4.4]EV[14th ch-kr Tengen, game 1]DT[2010-07-11]
PB[Chen Yaoye]BR[9p]PW[Park Junghwan]WR[8p]KM[6.5]RE[B+R]
;B[pd];W[dd];B[qp];W[dq];B[co];W[oq];B[mp];W[qf];B[pi];W[md];B[qe];W[pf];B[oe];W[pe]
;B[qd];W[od];B[of];W[og];B[nd];W[oc];B[nc];W[pc];B[rf];W[rg];B[ng];W[qc];B[rc];W[sf]
;B[rd];W[rb];B[ob];W[qb];B[pg];W[re];B[oh];W[oo];B[qn];W[pn];B[pm];W[om];B[pl];W[kp]
;B[mn];W[pp];B[ep];W[qq];B[qo];W[eq];B[fp];W[gq];B[gp];W[hq];B[hp];W[rq];B[iq];W[ir]
;B[jr];W[ip];B[jq];W[cq];B[nr];W[or];B[cf];W[di];B[ef];W[fc];B[dk];W[fi];B[gf];W[ek]
;B[el];W[dl];B[cl];W[dm];B[ck];W[fk];B[em];W[id];B[eh];W[ei];B[cc];W[cd];B[bd];W[dc]
;B[bb];W[cb];B[bc];W[mc];B[mb];W[ne];B[nb];W[kc];B[me];W[en];B[dn];W[ri];B[hk];W[hi]
;B[jd];W[ic];B[gd];W[gc];B[jk];W[fl];B[cm];W[ji];B[kd];W[le];B[jc];W[jf];B[mf];W[jb]
;B[kb];W[ib];B[np];W[ol];B[po];W[op];B[on];W[nn];B[no];W[pn];B[qg];W[rf];B[on];W[ce]
;B[be];W[pn];B[gh];W[fh];B[fg];W[hh];B[on];W[cg];B[bf];W[pn];B[bp];W[is];B[on];W[jp]
;B[lq];W[pn];B[br];W[cr];B[on];W[il];B[nm];W[jl];B[kl];W[kk];B[km];W[lk];B[hl];W[hm]
;B[jj];W[dg];B[df];W[kj];B[gm];W[kn];B[hn];W[jm];B[lm];W[ik];B[jn])
]]


EMPTY = 0
BLACK = 1
WHITE = 2
ONSTACK = 4
DEAD = 5

Status = {FirstMove = 1, LastMove = 999, FirstMoveLabel = 1, LastMoveLabel = 999} 

function displayMove (num)
	if num < Status.FirstMove or num > Status.LastMove then return false else return true end
end 


function writeLabel (num)
	if num < Status.FirstMoveLabel or num > Status.LastMoveLabel then return false else return true end
end


function setLabelBoundaries (start, stop)
	Status.FirstMoveLabel = start
	Status.LastMoveLabel = stop
end


function setMoveBoundaries (start, stop)
	Status.FirstMove = start
	Status.LastMove = stop
end

Board = { Size = 19, LastColor = WHITE, MoveNumber = 0, Label = {}; {}}

Big = { Board = 15, Grid = 0.7, Stone = 14.5, Label = 0.9 }		--> Big sized goban 
Medium = { Board = 12, Grid = 0.5, Stone = 11.5, Label = 0.7 }	--> Medium sized goban
Small = { Board = 10, Grid = 0.4, Stone = 9.5, Label = 0.55 }	 	--> Small sized goban

Unit = Big

Hoshi19 = {{x=4, y=4}, {x=4, y=10}, {x=4, y=16}, {x=10, y=4}, {x=10, y=10}, {x=10, y=16}, {x=16, y=4}, {x=16, y=10},{x=16, y=16}}
Hoshi13 = {{x=4, y=4}, {x=4, y=10}, {x=10, y=4}, {x=10, y=10}, {x=7, y=7}}
Hoshi9 = {{x=3, y=3}, {x=3, y=7}, {x=7, y=3}, {x=7, y=7}, {x=5, y=5}}

Hoshi = Hoshi19

---------------------------------------------------------
--- TeX (MetaPost) drawing functions
---------------------------------------------------------

function stone (x, y, c)	--> x, y: number; c: string (color: white or black)
	tex.sprint("fill fullcircle scaled " .. Unit.Stone .. " shifted (" .. x*Unit.Board .. "," .. y*-Unit.Board .. ") withcolor " .. c .. " ;")
	tex.sprint("draw fullcircle scaled " .. Unit.Stone .. " shifted (" .. x*Unit.Board .. "," .. y*-Unit.Board .. ") withcolor black ;")
end


function circleMarker (x, y, c)
	tex.sprint("draw fullcircle scaled " .. 0.6*Unit.Stone .. " shifted (" .. x*Unit.Board .. "," .. y*-Unit.Board .. ") withpen pencircle scaled (" .. 2*Unit.Grid .. ") withcolor " .. c .. " ;")
end


function label (l, x, y, c)
	tex.sprint("label(textext(\"\\color[" .. c .. "]{\\bf\\ssxx " .. l .. "}\"),(" .. x*Unit.Board .. ',' .. y*-Unit.Board .. '))  ;')
end


function drawGoban ()
	for i=1, Board.Size do
		tex.sprint("draw (" .. Unit.Board .. "," .. i*-Unit.Board .. ") -- (" .. Board.Size*Unit.Board .. "," .. i*-Unit.Board .. ") withpen pencircle scaled " .. Unit.Grid .. " ;")
		tex.sprint("draw (" .. i*Unit.Board .. "," .. -Unit.Board .. ") -- (" .. i*Unit.Board .. "," .. Board.Size*-Unit.Board .. ") withpen pencircle scaled " .. Unit.Grid .. " ;")
	end

	tex.sprint("draw (" .. Unit.Board .. "," .. -Unit.Board .. ") -- (" .. Board.Size*Unit.Board .. "," .. -Unit.Board ..") -- (" .. Board.Size*Unit.Board .. "," .. Board.Size*-Unit.Board .. ") -- (" .. Unit.Board .. "," .. Board.Size*-Unit.Board .. ") -- cycle withpen pencircle scaled (" ..2.5*Unit.Grid .. ") ;")

	for i=1, #Hoshi do
		x = Hoshi[i].x; y = Hoshi[i].y
		tex.sprint("drawdot (" .. x*Unit.Board .. "," .. y*-Unit.Board .. ") withpen pencircle scaled (" .. 5*Unit.Grid .. ") ;")
	end
end


function drawStones ()
	for i=1, Board.Size do
		for j=1, Board.Size do
			if Board[i][j] == BLACK then stone(i, j, "black") elseif Board[i][j] == WHITE then stone(i, j, "white") end
			if Board.Label[i][j] > 0 then
				if Board[i][j] == BLACK then
					label(Board.Label[i][j], i, j, "white")
				elseif Board[i][j] == WHITE then
					label(Board.Label[i][j], i, j, "black")
				end
			elseif Board.Label[i][j] < 0 then
				if Board[i][j] == BLACK then circleMarker(i, j, "white") else circleMarker(i, j, "black") end
			end
		end
	end
end


function displayBoard (name, size, p1, p2)

	if size == "small" then
		Unit = Small
	elseif size == "medium" then
		Unit = Medium
	else
		Unit = Big
	end

	tex.sprint("\\startuseMPgraphic{" .. name .. "}{}")
	tex.sprint("path p ;")

	drawGoban()
	drawStones()

	if p1 then
		local x1, y1 = Point(p1)
		local x2, y2 = Point(p2)
		tex.sprint("p:= (" .. (x1-1)*Unit.Board .. "," .. (y1-1)*-Unit.Board ..") -- (" .. x2*Unit.Board .. "," .. (y1-1)*-Unit.Board .. ") -- (" .. x2*Unit.Board .. "," .. y2*-Unit.Board .. ") -- (" .. (x1-1)*Unit.Board .. "," .. y2*-Unit.Board .. ") -- cycle ;")
		tex.sprint("clip currentpicture to p shifted (" .. Unit.Board/2 .. "," .. -Unit.Board/2 .. ") ;")
	end

	tex.sprint("\\stopuseMPgraphic")
	tex.sprint("\\useMPgraphic{" .. name .. "}")
end


function setStone (p, c)		--> c = "black", "white" or "empty" (AB, AW, AE)
	local x, y = Point(p)
	if c == "black" then
		Board[x][y] = BLACK 
	elseif c == "white" then
		Board[x][y] = WHITE
	elseif c == "empty" then
		Board[x][y] = EMPTY
	end
end


function Point (p)
	local x, y = 0, 0
	local l = "abcdefghijklmnopqrstuvwxyz"
	x = string.find(l, string.sub(p, 1, 1))
	y = string.find(l, string.sub(p, 2, 2))
	return x, y
end


function setMarker (p)
	local x, y = Point(p)
	if Board[x][y] > 0 then Board.Label[x][y] = -1 end
end


function playMove (p, c)		--> if p not defined -> pass
	Board.MoveNumber = Board.MoveNumber + 1	
	if displayMove(Board.MoveNumber) then
		if c then
			if c == "white" then Board.LastColor = WHITE else Board.LastColor = BLACK end
		else
			if Board.LastColor == BLACK then Board.LastColor = WHITE else Board.LastColor = BLACK end
		end
		if p then
			local x, y = Point(p)
			Board[x][y] = Board.LastColor
			if writeLabel(Board.MoveNumber) then 
				Board.Label[x][y] = Board.MoveNumber
			else
				testDeadStones(x, y)	--> only test dead stones if you dont write label
			end
-- here should be test for suicide move, allowed on some rules version 
		end	
	end
end


function setBoardSize (s)
	if s == "small" then
		Board.Size = 9
		Hoshi = Hoshi9
	elseif s == "medium" then
		Board.Size = 13
		Hoshi=Hoshi13
	else
		Board.Size = 19
		Hoshi = Hoshi19
	end
	setLabelBoundaries(1, 999)
end


function resetBoard ()
	Board.LastColor = WHITE
	Board.MoveNumber = 0
	for i=1, Board.Size do
		Board[i] = {}
		Board.Label[i] = {}
		for j=1, Board.Size do
			Board[i][j] = EMPTY 
			Board.Label[i][j] = EMPTY
		end
	end
end


---------------------------------------------------------
--- Deadstone calculator
---------------------------------------------------------

--- Stack implementation for deadstone calculator

function resetStack ()
	stack = {}
end


function push (x, y)
	stack = {next = stack, x = x, y = y}
end


function pop ()
	if stack then
		local x, y = stack.x, stack.y
		stack = stack.next
		return x, y
	end
end


--- Deadstone calculator

--- Are x, y valid coordinates? 

function isValid (x, y)
	if x >= 1 and x <= Board.Size and y >= 1 and y <= Board.Size then return true end
	return false
end

--- Reset shadow Goban

shadowboard = {}

function resetShadowBoard (size)
	for i = 1, size do
		shadowboard[i] = {}
	end
end

--- Na základě stínového gobanu odstraní kameny označené jako mrtvé ze skutečného gobanu.

function removeDeadStones ()
	for i, k in pairs(shadowboard) do
		for j, v in pairs(k) do Board[i][j] = EMPTY end
	end
end

--- Test, je-li x, y prázdná.
-- V opačném případě je-li na x, y soupeřův kámen, uloží do zásobníku jeho pozici.

function isEmpty (x, y)
	if Board[x][y] == EMPTY then
		return true
	elseif Board[x][y] ~= myColor and shadowboard[x][y] ~= DEAD then
		push(x, y)
	end
	return false
end

--- Testuje, zda má kámen na pozici x, y svobodu.
-- V případě že ne, vrací true, jinak false.

function isDead (x, y)
	if isValid(x, y+1) and isEmpty(x, y+1) then return false end
	if isValid(x-1, y) and isEmpty(x-1, y) then return false end
	if isValid(x, y-1) and isEmpty(x, y-1) then return false end
	if isValid(x+1, y) and isEmpty(x+1, y) then return false end
	return true
end

--- Test for dead group.
-- If the group with stone on (x, y) is dead, it removes it from goban. 
-- Can be used to test for suicide.

function deadStones (x, y)
	resetStack()
	resetShadowBoard(Board.Size)
	while isDead(x, y) do
		shadowboard [x][y] = DEAD
		x, y = pop()
		if not x then
			removeDeadStones()
			break
		end
	end
end

--- Main entry point for deadstone calculator.
-- For each neighbour stone it starts test for dead group.

function testDeadStones (x, y)
	myColor = Board[x][y]
	if isValid(x+1, y) and Board[x+1][y] ~= EMPTY and Board[x+1][y] ~= myColor then deadStones(x+1, y) end
	if isValid(x, y+1) and Board[x][y+1] ~= EMPTY and Board[x][y+1] ~= myColor then deadStones(x, y+1) end
	if isValid(x-1, y) and Board[x-1][y] ~= EMPTY and Board[x-1][y] ~= myColor then deadStones(x-1, y) end
	if isValid(x, y-1) and Board[x][y-1] ~= EMPTY and Board[x][y-1] ~= myColor then deadStones(x, y-1) end
end


---------------------------------------------------------
--- SGF parser
---------------------------------------------------------

--- Very ugly looking first implementation of SGF parser mainly for testing deadstone calculator


function blackmove (a, b, c)
	playMove(c, "black")
	return b
end


function whitemove (a, b, c)
	playMove(c, "white")
	return b
end


function blackstone (a, b, c)
	print("\\stone[black]{"..c.."}")
	return b
end


function whitestone (a, b, c)
	print("\\stone[white]{"..c.."}")
	return b
end


function emptystone (a, b, c)
	print("\\stone[empty]{"..c.."}")
	return b
end


function parseSGF(SGFstring)
	emptychar = (lpeg.S(" \t\r\n"))^1
	letter = lpeg.R("az", "AZ")
	number = (lpeg.R("09"))^1
	text = (letter + number + lpeg.S(" .,:;+-=_?!@#$%^&*()/"))^1

	axis = letter * letter
	shortparam = axis + ""
	longparam = axis * ( ":" * axis )^-1

	FF = lpeg.P("FF[4]") * emptychar^0
	GM = lpeg.P("GM[1]") * emptychar^0
	SZ = lpeg.P("SZ[") * number * lpeg.P("]") * emptychar^0
	DT = lpeg.P("DT[") * text * lpeg.P("]") * emptychar^0
	EV = lpeg.P("EV[") * text * lpeg.P("]") * emptychar^0
	PB = lpeg.P("PB[") * text * lpeg.P("]") * emptychar^0
	PW = lpeg.P("PW[") * text * lpeg.P("]") * emptychar^0
	BR = lpeg.P("BR[") * text * lpeg.P("]") * emptychar^0
	WR = lpeg.P("WR[") * text * lpeg.P("]") * emptychar^0
	KM = lpeg.P("KM[") * text * lpeg.P("]") * emptychar^0
	RE = lpeg.P("RE[") * text * lpeg.P("]") * emptychar^0
	CA = lpeg.P("CA[") * text * lpeg.P("]") * emptychar^0
	AP = lpeg.P("AP[") * text * lpeg.P("]") * emptychar^0
	DT = lpeg.P("DT[") * text * lpeg.P("]") * emptychar^0



	B = lpeg.P("B[") * lpeg.Cmt(shortparam, blackmove) * lpeg.P("]")	* emptychar^0
	W = lpeg.P("W[") * lpeg.Cmt(shortparam, whitemove) * lpeg.P("]") * emptychar^0

	rootproperty = FF + GM + SZ + DT + EV + PB + PW + BR + WR + KM + RE + CA + AP + DT
	property = B + W

	rootnode = lpeg.P(";") * (rootproperty)^1
	node = lpeg.P(";") * property

	sequence = rootnode^0 * node^1

	gameTree = lpeg.P("(") * sequence^1 * lpeg.P(")")

--	tex.sprint("SGFparse: " .. SGFstring)
--	tex.sprint("::")
--	tex.sprint()

	resetBoard ()
	if not lpeg.match(gameTree, SGFstring) then
		tex.sprint("Invalid SGF file")
	end

end


\stopluacode

\def\move#1%
	{\ctxlua{playMove("#1")}}

%\def\move
%	{\dosingleargument\domove}

%\def\domove[#1]{#2}
%	{\iffirstargument
%		\ctxlua{playMove("#2", "#1")}
%	\else
%		\ctxlua{playMove("#2")}
%	\fi}

\def\pass
	{\ctxlua{playMove()}}

\def\stone[#1]#2%
	{\ctxlua{setStone("#2", "#1")}}

\def\marker#1%
	{\ctxlua{setMarker("#1")}}

\def\newboard
	{\dosingleargument\donewboard}

\def\donewboard[#1]%
	{\iffirstargument
		\ctxlua{setBoardSize("#1")}
	\else
		\ctxlua{setBoardSize("big")}
	\fi}

\def\resetnumbering
	{\ctxlua{resetBoard()}}

\def\drawboard
	{\dosingleargument\dodrawboard}

\def\dodrawboard[#1]%
	{\iffirstargument
		\ctxlua{displayBoard(0, "#1")}
	\else
		\ctxlua{displayBoard(0, "medium")}
	\fi}

\def\drawpartialboard#1#2%
	{\ctxlua{displayBoard(0, "medium", "#1", "#2")}}

\def\parse#1%
	{\ctxlua{parseSGF("#1")}}

\def\position
	{\dosingleargument\doposition}

\def\doposition[#1]%
	{\iffirstargument
		\ctxlua{setMoveBoundaries(1, #1); parseSGF(Game)}
	\else
		{\ctxlua{parseSGF(Game)}}
	\fi}

\def\labels#1#2%
	{\ctxlua{setLabelBoundaries(#1, #2)}}


\starttext


\newboard

\position[40]
\drawboard

\labels{41}{999}
\position[100]
\drawboard[small]

\labels{101}{999}
\position[999]
\drawboard[big]

\labels{999}{999}
\position{999}
\drawboard

The annual bilateral Tengen championship is back. This year, Park Junghwan Chunwon (Korean for Tengen) meet Chen Yaoye Tianyuan (Chinese for Tengen). This tournament is sponsored by Sports Korea newspaper and Xinmin Wanbo evening paper.

Chen Yaoye of China opened the first game by an early 165 moves resignation game. Chen who played black in the first game ended the game by cutting Park's group. The cut will lead to a favorable ko fight for black. I'm not sure whether white group can survive here or not, but perhaps Park didn't think he'll win the fight, so he resigned.

% \newboard
% \parse{;B[pd];W[dd];B[pq];W[dp];B[fq];W[cn];B[qk];W[jp];B[dr];W[gq];B[fp];W[fr];B[er];W[gr]
% ;B[cp];W[cq];B[bq];W[bp];B[co];W[bo];B[do];W[br];B[dq];W[cr];B[dn];W[mq];B[cm];W[bn]
% ;B[po];W[dm];B[nc];W[fc];B[be];W[ce];B[bd];W[bf];B[cf];W[cg];B[df];W[bg];B[dg];W[dh]
% ;B[eh];W[di];B[dc];W[cc];B[cb];W[cd];B[bc];W[db];B[ec];W[ed];B[eb];W[eg];B[ef];W[fe]
% ;B[gd];W[fd];B[da];W[fg]}
% \drawboard
% \clearnumbers
% \parse{;B[em];W[dl];B[qe];W[fm];B[fn];W[el];B[gm];W[en]}
% \drawboard
% \clearnumbers
% \parse{;B[eo];W[ok]
% ;B[oj];W[nj];B[pj];W[nk];B[ni];W[mi];B[mh];W[li];B[pm];W[qc];B[em]}
% \drawboard
% \clearnumbers
% \parse{;W[re];B[rd];W[qd]
% ;B[rc];W[nh];B[oi];W[pe];B[qf];W[pc];B[od];W[rf];B[rb];W[qg];B[pf];W[of];B[pg];W[ph]
% ;B[og];W[oh];B[oe]}
% \drawboard
% \clearnumbers
% \parse{;W[qi];B[ng];W[rk];B[rl];W[rj];B[ql];W[rh];B[fl];W[jd]}
% \drawboard
% \clearnumbers
% \parse{;B[lh];W[kh]
% ;B[lf];W[ki];B[jf];W[he];B[kc];W[kd];B[jc];W[ic];B[ib];W[le];B[kg];W[lc];B[lb];W[kb]
% ;B[jb];W[mb];B[ka];W[mc];B[ob];W[ig]}
% \drawboard
% \clearnumbers
% \parse{;B[ej];W[dj];B[fk];W[ek];B[lr];W[mr];B[iq];W[ip]
% ;B[hp];W[jq];B[hq];W[jr];B[mo];W[or];B[pr];W[lp];B[ir];W[jg];B[me];W[fb];B[hb];W[fa]
% ;B[ba];W[is];B[hs];W[js];B[gp];W[sl];B[sm];W[sk];B[rn];W[ps];B[qs];W[os];B[qr];W[if]
% ;B[gi];W[md];B[mf];W[hj];B[ei];W[nm];B[jl];W[hl];B[lm];W[kl]}
% \drawboard
% \clearnumbers
% \parse{;B[km];W[jm];B[kk];W[ll]
% ;B[ml];W[lk];B[mk];W[lj];B[mm];W[mj];B[mp];W[lo];B[nq];W[mn];B[nn];W[ln];B[lq];W[ls]
% ;B[nr];W[ms];B[jn];W[im];B[cl];W[dk];B[ns];W[no];B[ks];W[on];B[kp];W[ko];B[in];W[hm]
% ;B[hn];W[kn]}
% \drawboard
% \clearnumbers
% \parse{;B[hi];W[pn];B[qn];W[op];B[oq];W[pp];B[qp];W[ii]}
% \drawboard
% \clearnumbers
% \parse{;B[ih];W[gj];B[fj];W[ji]
% ;B[sd];W[se];B[hg];W[ff];B[jo];W[kq];B[io];W[nl]}
% \drawboard
% \clearnumbers
% \parse{;B[hd];W[id];B[gb];W[na];B[la];W[oa]
% ;B[pa];W[nd];B[nb];W[ma];B[pb];W[fh];B[fi];W[ea];B[db];W[ge];B[ne];W[ld];B[pi];W[qh]
% ;B[kr];W[kp];B[gk];W[hk];B[ds];W[cs];B[af];W[ag];B[ae];W[ga];B[ha];W[np];B[mq];W[pk]
% ;B[qj];W[pl];B[om];W[gl];B[hh];W[jh];B[qm];W[nn];B[qo];W[hc];B[gc];W[ie];B[fm])}
% \drawboard
% 
Park Junghwan even the game by winning the second game by 2.5 points. The game was quite territory oriented. Black claimed the right side, while white went for the left side and aimed for the upper board. There was a big trade at move 205. White sacrificed his 7 stones (about 12 points) for black's 5 stones. The trade was even I think. However, black eventually came out ahead after the trade.



\stoptext

% ;FF[4];AW[gg:hh][ij][kl][op]AB[kk][gh]AB[ll]AW[oo]AE[hg];B[dd]BT[567];W[cc]WT[1234];B[];W[];B[dc];W[cd];B[ce];W[be];B[cf];W[bf];B[cg]